package com.lin.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.lin.constant.BookConstants;
import com.lin.entity.R;
import com.lin.entity.dto.BookDTO;
import com.lin.entity.dto.UpdateBookDTO;
import com.lin.entity.po.Book;
import com.lin.entity.query.BookQuery;
import com.lin.entity.vo.BookVO;
import com.lin.entity.vo.PageVO;
import com.lin.exception.BookISBNException;
import com.lin.exception.BookStatusException;
import com.lin.mapper.BookMapper;
import com.lin.service.IBookService;
import com.lin.utils.RedisSerializerUtil;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService {
    @Resource
    private RedisSerializerUtil redisUtil;

    /**
     * 新增图书
     *
     * @param bookDTO 图书信息
     * @return R
     */
    @Override
    public R addBook(BookDTO bookDTO) {
        if (!bookDTO.isEmpty()) {
            String isbn = bookDTO.getIsbn();
            Book one = lambdaQuery().eq(Book::getIsbn, isbn).one();
            if (one != null)
                throw new BookISBNException("ISBN已存在");
            if (save(BeanUtil.copyProperties(bookDTO, Book.class)))
                return R.ok("新增图书成功", null);
            return R.failed("新增图书失败", null);
        }
        return R.failed("图书信息不能为空", null);
    }


    /**
     * 修改图书
     * @param updateBookDTO 图书信息
     * @return R
     */
    @Override
    public R updateBook(UpdateBookDTO updateBookDTO) {
        if (!updateBookDTO.isBlank()) {
            Book book = getById(updateBookDTO.getId());

            if (book.getStatus().equals(BookConstants.LOGIC_DELETE_BOOK_STATUS)) {
                throw new BookStatusException("图书不存在");
            }
            if (updateById(BeanUtil.copyProperties(updateBookDTO, Book.class)))
                return R.ok("修改图书成功", null);
            return R.failed("修改图书失败", null);
        }
        return R.failed("图书信息不能为空", null);
    }

    /**
     * 获取图书信息--先查询Redis中是否有对应数据, 存在直接返回
     * 不存在则查询数据库并添加到Redis中
     * @param bookId 图书id
     * @return VO
     */
    @Override
    public BookVO getBookById(Integer bookId) throws JsonProcessingException {
        // 当前图书信息key
        String BookInfoKey = BookConstants.BOOK_INFO_KEY_PREFIX + bookId;

        // 先查看Redis中是否已有数据
        Book book = redisUtil.get(BookInfoKey, Book.class);

        // 如果Redis中存在数据, 直接返回
        if (book != null)
            return BeanUtil.copyProperties(book, BookVO.class);

        // Redis中没有数据, 从数据库中查询并存到Redis
        book = getById(bookId);

        // 判断图书是否存在数据库
        if (null != book) {
            if (book.getStatus().equals(BookConstants.LOGIC_DELETE_BOOK_STATUS))
                throw new BookStatusException("图书不存在");
            BookVO bookVO = BeanUtil.copyProperties(book, BookVO.class);
            redisUtil.set(BookInfoKey, bookVO); // 将查询结果存储到Redis中
            return bookVO;
        }
        return new BookVO();
    }

    /**
     * 获取图书列表--基础分页
     * 先查询Redis中是否有对应数据, 存在直接返回
     * 不存在则查询数据库并添加到Redis中
     * @param pageParam 当前页码
     * @param pageSize  每页数量
     * @return VO
     */
    @Override
    public PageVO<BookVO> getBookList(int pageParam, int pageSize) throws JsonProcessingException {
        // 当前图书信息key
        String BookInfoKey = BookConstants.BOOK_PAGE_KEY_PREFIX + pageParam + "_" + pageSize;

        // 预声明返回结果
        PageVO<BookVO> pageVO = new PageVO<>();
        // 分页参数
        Page<Book> page = Page.of(pageParam, pageSize);
        // 设置分页信息
        setPageVO(pageVO, page);

        // 先查看Redis中是否已有数据
        List<Book> books = redisUtil.get(BookInfoKey, new TypeReference<>() {});

        // 如果Redis中存在数据, 直接返回
        if (CollUtil.isNotEmpty(books)) {
            // 转换为BookVO实体
            List<BookVO> bookVOS1 = BeanUtil.copyToList(books, BookVO.class);
            pageVO.setList(bookVOS1); // 设置分页数据
            return pageVO;
        }

        // 分页条件
        LambdaQueryWrapper<Book> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ne(Book::getStatus, BookConstants.LOGIC_DELETE_BOOK_STATUS);

        // 分页查询
        Page<Book> p = this.page(page, queryWrapper);

        // 获取记录
        List<Book> records = p.getRecords();

        // 封装为BookVO实体并设置分页数据
        if (records != null && !records.isEmpty()) {
            // 封装为BookVO实体
            List<BookVO> bookVOS = BeanUtil.copyToList(records, BookVO.class);
            pageVO.setList(bookVOS); // 设置分页数据
            redisUtil.set(BookInfoKey, bookVOS); // 将查询结果存储到Redis中
        }
        return pageVO;
    }

    /**
     * 获取图书列表--高级分页
     * 先查看Redis中是否有对应数据, 存在直接返回
     * 不存在则查询数据库并添加到Redis中
     * @param bookQuery 查询条件
     * @return List<BookVO>
     */
    @Override
    public PageVO<BookVO> getBooksPage(BookQuery bookQuery) throws JsonProcessingException {
        return getBooksPage(bookQuery, true);
    }

    /**
     * 获取图书列表--高级分页
     * 先查看Redis中是否有对应数据, 存在直接返回
     * 不存在则查询数据库并添加到Redis中
     * @param bookQuery 查询条件
     * @param orderBy   按字符顺序排序 or 按字符长度排序; 默认按字符顺序
     * @return List<BookVO>
     */
    @Override
    public PageVO<BookVO> getBooksPageByCharLen(BookQuery bookQuery, boolean orderBy) throws JsonProcessingException {
        return getBooksPage(bookQuery, orderBy);
    }

    public PageVO<BookVO> getBooksPage(BookQuery bookQuery, boolean orderBy) throws JsonProcessingException {
        // 当前图书信息key
        String BookInfoKey = BookConstants.BOOK_PAGE_KEY_PREFIX + bookQuery.getPage() + "_" + bookQuery.getPageSize();

        // 预声明返回结果
        PageVO<BookVO> pageVO = new PageVO<>();
        // 分页参数
        Page<Book> page = Page.of(bookQuery.getPage(), bookQuery.getPageSize());
        // 设置分页信息
        setPageVO(pageVO, page);


        // 先查看Redis中是否有数据, 存在直接返回
        List<Book> books = redisUtil.get(BookInfoKey, new TypeReference<>() {});

        // 如果Redis中存在数据, 直接返回
        if (CollUtil.isNotEmpty(books)) {
            // 转换为BookVO实体
            List<BookVO> bookVOS1 = BeanUtil.copyToList(books, BookVO.class);
            pageVO.setList(bookVOS1); // 设置分页数据
            return pageVO;
        }

        // 分页条件
        LambdaQueryWrapper<Book> bookLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 图书状态不为0
        bookLambdaQueryWrapper.ne(Book::getStatus, BookConstants.LOGIC_DELETE_BOOK_STATUS);

        // 排序条件
        String sortBy = bookQuery.getSortBy();

        // 查询条件
        String isbn = bookQuery.getIsbn();
        LocalDate publishDate = bookQuery.getPublishDate();
        String direction = bookQuery.getIsAsc() ? "ASC" : "DESC"; // 默认降序

        // 默认按照图书发布时间降序排序
        // 如果排序字段是字符类型, 会默认根据字母顺序排序, 而不是字符串长度
        if (orderBy) {
            if (StrUtil.isNotBlank(sortBy)) {
                if (bookQuery.getIsAsc()) page.addOrder(OrderItem.asc(sortBy)); // 升序
                else page.addOrder(OrderItem.desc(sortBy)); // 降序
            } else page.addOrder(OrderItem.desc("publish_date"));
        } else {
            // 根据字符串长度排序
            if (StrUtil.isNotBlank(sortBy)) {
                // 根据字符长度排序
                String orderByClause = String.format("CHAR_LENGTH(%s) %s", sortBy, direction);
                bookLambdaQueryWrapper.last("ORDER BY " + orderByClause);
            }
        }

        // isbn不为空, publishDate大于2015年
        if (isbn != null && !isbn.trim().isEmpty())
            bookLambdaQueryWrapper.isNotNull(Book::getIsbn);

        if (publishDate != null)
            bookLambdaQueryWrapper.gt(Book::getPublishDate, publishDate);
        else
            bookLambdaQueryWrapper.isNotNull(Book::getPublishDate);

        // Redis中没有对应数据, 分页查询数据库
        Page<Book> p = page(page, bookLambdaQueryWrapper);

        // 封装结果
        List<Book> records = p.getRecords();

        if (CollUtil.isNotEmpty(records)) {
            // 转换为BookVO实体
            List<BookVO> bookVOS = BeanUtil.copyToList(records, BookVO.class);
            pageVO.setList(bookVOS); // 设置分页数据
            redisUtil.set(BookInfoKey, bookVOS); // 将查询结果存储到Redis中
        }
        return pageVO;
    }

    private void setPageVO(PageVO<BookVO> pageVO, Page<Book> page) {
        pageVO.setPageNum((int) page.getCurrent());
        pageVO.setPageSize((int) page.getSize());
        pageVO.setTotal((int) page.getTotal());
    }

    /**
     * 逻辑删除图书
     *
     * @param id 图书ID
     * @return T/F
     */
    @Override
    public boolean logicDeleteBook(Integer id) {
        Book book = getById(id);
        if (book != null) {
            Integer status = book.getStatus();
            if (status == 0)
                throw new BookStatusException("图书不存在");
            else
                return lambdaUpdate()
                        .eq(Book::getId, id)
                        .set(Book::getStatus, BookConstants.LOGIC_DELETE_BOOK_STATUS)
                        .update();
        }
        return false;
    }
}
